package com.example.redislearning.service.impl;

import com.example.redislearning.service.NotificationService;
import com.example.redislearning.util.RedisKeyUtil;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.stereotype.Service;

import java.time.LocalDateTime;
import java.util.*;
import java.util.concurrent.TimeUnit;

/**
 * 消息通知服务实现类
 * 基于Redis实现系统公告、用户消息、学习提醒等功能
 */
@Service
public class NotificationServiceImpl implements NotificationService {

    @Autowired
    private RedisTemplate<String, Object> redisTemplate;

    @Autowired
    private RedisKeyUtil redisKeyUtil;

    private static final String MESSAGE_TYPE_SYSTEM = "SYSTEM";
    private static final String MESSAGE_TYPE_COURSE = "COURSE";
    private static final String MESSAGE_TYPE_STUDY = "STUDY";
    private static final String MESSAGE_TYPE_PROMOTION = "PROMOTION";

    private static final int DEFAULT_TTL_DAYS = 30;

    /**
     * 发布系统公告
     * @param title 公告标题
     * @param content 公告内容
     * @param publisher 发布者
     * @return 是否发布成功的布尔值
     * @throws RuntimeException 当发布系统公告失败时抛出异常
     */
    @Override
    public boolean publishAnnouncement(String title, String content, String publisher) {
        try {
            String announcementId = UUID.randomUUID().toString();
            String key = redisKeyUtil.getSystemAnnouncementKey();
            
            Map<String, Object> announcement = new HashMap<>();
            announcement.put("id", announcementId);
            announcement.put("title", title);
            announcement.put("content", content);
            announcement.put("publisher", publisher);
            announcement.put("publishTime", LocalDateTime.now().toString());
            announcement.put("type", MESSAGE_TYPE_SYSTEM);
            
            // 使用Redis List存储公告，按时间倒序
            redisTemplate.opsForList().leftPush(key, announcement);
            
            // 设置过期时间
            redisTemplate.expire(key, DEFAULT_TTL_DAYS, TimeUnit.DAYS);
            
            return true;
        } catch (Exception e) {
            throw new RuntimeException("发布系统公告失败", e);
        }
    }

    /**
     * 获取系统公告列表
     * @param page 页码（从1开始）
     * @param size 每页数量
     * @return 系统公告列表，每个公告包含标题、内容、发布者、发布时间等信息
     * @throws RuntimeException 当获取系统公告失败时抛出异常
     */
    @Override
    public List<Map<String, Object>> getAnnouncements(int page, int size) {
        try {
            String key = redisKeyUtil.getSystemAnnouncementKey();
            Integer start = (page - 1) * size;
            Integer end = start + size - 1;
            
            List<Object> announcements = redisTemplate.opsForList().range(key, start, end);
            List<Map<String, Object>> result = new ArrayList<>();
            
            if (announcements != null) {
                for (Object announcement : announcements) {
                    if (announcement instanceof Map) {
                        result.add((Map<String, Object>) announcement);
                    }
                }
            }
            
            return result;
        } catch (Exception e) {
            throw new RuntimeException("获取系统公告失败", e);
        }
    }

    /**
     * 发送用户消息
     * @param userId 用户ID
     * @param title 消息标题
     * @param content 消息内容
     * @param type 消息类型（SYSTEM/COURSE/STUDY/PROMOTION）
     * @return 是否发送成功的布尔值
     * @throws RuntimeException 当发送用户消息失败时抛出异常
     */
    @Override
    public boolean sendUserMessage(Integer userId, String title, String content, String type) {
        try {
            String messageId = UUID.randomUUID().toString();
            String key = redisKeyUtil.getUserMessageQueueKey(userId);
            
            Map<String, Object> message = new HashMap<>();
            message.put("id", messageId);
            message.put("title", title);
            message.put("content", content);
            message.put("type", type);
            message.put("createTime", LocalDateTime.now().toString());
            message.put("read", false);
            
            // 使用Redis List存储用户消息
            redisTemplate.opsForList().leftPush(key, message);
            
            // 设置过期时间
            redisTemplate.expire(key, DEFAULT_TTL_DAYS, TimeUnit.DAYS);
            
            // 增加未读消息计数
            String unreadKey = redisKeyUtil.getUserUnreadCountKey(userId);
            redisTemplate.opsForValue().increment(unreadKey);
            redisTemplate.expire(unreadKey, DEFAULT_TTL_DAYS, TimeUnit.DAYS);
            
            return true;
        } catch (Exception e) {
            throw new RuntimeException("发送用户消息失败", e);
        }
    }

    /**
     * 获取用户消息列表
     * @param userId 用户ID
     * @param page 页码（从1开始）
     * @param size 每页数量
     * @return 用户消息列表，每条消息包含标题、内容、类型、创建时间、是否已读等信息
     * @throws RuntimeException 当获取用户消息失败时抛出异常
     */
    @Override
    public List<Map<String, Object>> getUserMessages(Integer userId, int page, int size) {
        try {
            String key = redisKeyUtil.getUserMessageQueueKey(userId);
            Integer start = (page - 1) * size;
            Integer end = start + size - 1;
            
            List<Object> messages = redisTemplate.opsForList().range(key, start, end);
            List<Map<String, Object>> result = new ArrayList<>();
            
            if (messages != null) {
                for (Object message : messages) {
                    if (message instanceof Map) {
                        result.add((Map<String, Object>) message);
                    }
                }
            }
            
            return result;
        } catch (Exception e) {
            throw new RuntimeException("获取用户消息失败", e);
        }
    }

    /**
     * 标记消息为已读
     * @param userId 用户ID
     * @param messageId 消息ID
     * @return 是否成功标记的布尔值
     * @throws RuntimeException 当标记消息已读失败时抛出异常
     */
    @Override
    public boolean markMessageAsRead(Integer userId, String messageId) {
        try {
            String key = redisKeyUtil.getUserMessageQueueKey(userId);
            List<Object> messages = redisTemplate.opsForList().range(key, 0, -1);
            
            if (messages != null) {
                for (int i = 0; i < messages.size(); i++) {
                    Object messageObj = messages.get(i);
                    if (messageObj instanceof Map) {
                        Map<String, Object> message = (Map<String, Object>) messageObj;
                        if (messageId.equals(message.get("id"))) {
                            message.put("read", true);
                            redisTemplate.opsForList().set(key, i, message);
                            
                            // 减少未读消息计数
                            String unreadKey = redisKeyUtil.getUserUnreadCountKey(userId);
                            Integer unreadCount = (Integer) redisTemplate.opsForValue().get(unreadKey);
                            if (unreadCount != null && unreadCount > 0) {
                                redisTemplate.opsForValue().decrement(unreadKey);
                            }
                            
                            return true;
                        }
                    }
                }
            }
            
            return false;
        } catch (Exception e) {
            throw new RuntimeException("标记消息已读失败", e);
        }
    }

    /**
     * 获取用户未读消息数量
     * @param userId 用户ID
     * @return 未读消息数量（没有时返回0）
     * @throws RuntimeException 当获取未读消息数量失败时抛出异常
     */
    @Override
    public Integer getUnreadMessageCount(Integer userId) {
        try {
            String unreadKey = redisKeyUtil.getUserUnreadCountKey(userId);
            Integer count = (Integer) redisTemplate.opsForValue().get(unreadKey);
            return count != null ? count : 0;
        } catch (Exception e) {
            throw new RuntimeException("获取未读消息数量失败", e);
        }
    }

    /**
     * 发送学习提醒
     * @param userId 用户ID
     * @param courseId 课程ID
     * @param courseName 课程名称
     * @return 是否发送成功的布尔值
     */
    @Override
    public boolean sendStudyReminder(Integer userId, Integer courseId, String courseName) {
        String title = "学习提醒";
        String content = String.format("您有课程《%s》需要继续学习，当前学习进度为%d%%", 
                courseName, getCourseProgress(userId, courseId));
        
        return sendUserMessage(userId, title, content, MESSAGE_TYPE_STUDY);
    }

    /**
     * 发送课程更新通知
     * @param userId 用户ID
     * @param courseName 课程名称
     * @param updateContent 更新内容描述
     * @return 是否发送成功的布尔值
     */
    @Override
    public boolean sendCourseUpdateNotification(Integer userId, String courseName, String updateContent) {
        String title = "课程更新通知";
        String content = String.format("您关注的课程《%s》已更新：%s", courseName, updateContent);
        
        return sendUserMessage(userId, title, content, MESSAGE_TYPE_COURSE);
    }

    /**
     * 发送促销通知
     * @param userId 用户ID
     * @param promotionTitle 促销标题
     * @param promotionContent 促销内容
     * @return 是否发送成功的布尔值
     */
    @Override
    public boolean sendPromotionNotification(Integer userId, String promotionTitle, String promotionContent) {
        return sendUserMessage(userId, promotionTitle, promotionContent, MESSAGE_TYPE_PROMOTION);
    }

    /**
     * 删除用户消息
     * @param userId 用户ID
     * @param messageId 消息ID
     * @return 是否成功删除的布尔值
     * @throws RuntimeException 当删除用户消息失败时抛出异常
     */
    @Override
    public boolean deleteUserMessage(Integer userId, String messageId) {
        try {
            String key = redisKeyUtil.getUserMessageQueueKey(userId);
            List<Object> messages = redisTemplate.opsForList().range(key, 0, -1);
            
            if (messages != null) {
                for (int i = 0; i < messages.size(); i++) {
                    Object messageObj = messages.get(i);
                    if (messageObj instanceof Map) {
                        Map<String, Object> message = (Map<String, Object>) messageObj;
                        if (messageId.equals(message.get("id"))) {
                            // 如果消息未读，减少未读计数
                            Boolean read = (Boolean) message.get("read");
                            if (read == null || !read) {
                                String unreadKey = redisKeyUtil.getUserUnreadCountKey(userId);
                                Integer unreadCount = (Integer) redisTemplate.opsForValue().get(unreadKey);
                                if (unreadCount != null && unreadCount > 0) {
                                    redisTemplate.opsForValue().decrement(unreadKey);
                                }
                            }
                            
                            // 从列表中移除消息
                            redisTemplate.opsForList().remove(key, 1, messageObj);
                            return true;
                        }
                    }
                }
            }
            
            return false;
        } catch (Exception e) {
            throw new RuntimeException("删除用户消息失败", e);
        }
    }

    /**
     * 批量标记消息已读
     * @param userId 用户ID
     * @return 是否成功标记的布尔值
     * @throws RuntimeException 当批量标记消息已读失败时抛出异常
     */
    @Override
    public boolean markAllMessagesAsRead(Integer userId) {
        try {
            String key = redisKeyUtil.getUserMessageQueueKey(userId);
            List<Object> messages = redisTemplate.opsForList().range(key, 0, -1);
            
            if (messages != null) {
                for (int i = 0; i < messages.size(); i++) {
                    Object messageObj = messages.get(i);
                    if (messageObj instanceof Map) {
                        Map<String, Object> message = (Map<String, Object>) messageObj;
                        message.put("read", true);
                        redisTemplate.opsForList().set(key, i, message);
                    }
                }
            }
            
            // 重置未读计数
            String unreadKey = redisKeyUtil.getUserUnreadCountKey(userId);
            redisTemplate.opsForValue().set(unreadKey, 0L, DEFAULT_TTL_DAYS, TimeUnit.DAYS);
            
            return true;
        } catch (Exception e) {
            throw new RuntimeException("批量标记消息已读失败", e);
        }
    }

    /**
     * 清理过期消息
     */
    @Override
    public boolean cleanExpiredMessages(Integer userId) {
        try {
            String key = redisKeyUtil.getUserMessageQueueKey(userId);
            String unreadKey = redisKeyUtil.getUserUnreadCountKey(userId);
            
            // 直接删除用户消息队列
            redisTemplate.delete(key);
            redisTemplate.delete(unreadKey);
            
            return true;
        } catch (Exception e) {
            throw new RuntimeException("清理过期消息失败", e);
        }
    }

    /**
     * 获取课程进度（内部方法）
     */
    private int getCourseProgress(Integer userId, Integer courseId) {
        try {
            String progressKey = redisKeyUtil.getUserCourseProgressKey(userId, courseId);
            Object progress = redisTemplate.opsForHash().get(progressKey, "progress");
            return progress != null ? (Integer) progress : 0;
        } catch (Exception e) {
            return 0;
        }
    }

    /**
     * 生成消息ID
     * @return 生成的消息ID字符串
     */
    private String generateMessageId() {
        return UUID.randomUUID().toString().replace("-", "");
    }

    /**
     * 获取当前时间戳
     * @return 当前时间戳（毫秒）
     */
    private long getCurrentTimestamp() {
        return System.currentTimeMillis();
    }
}